/*
Copyright 2011 Francisco Nieto Raya

This program is free software: you can redistribute it and/or modify it under the terms of the Apache Software License 
as published by the Free Software Foundation, either version 2.0 of the License, or (at your option) any later version.

This software consists of voluntary contributions made by many individuals on behalf of the Apache Software Foundation.
For more information on the Apache Software Foundation, please see <http://www.apache.org/>.

*/

#include "imag.h"

/* Esta función reserva memoria para una imagen. Primero 
   reserva para la imagen, luego reserva para las filas 
   y las columnas de la matriz de pixeles. 
   Recibe:
      fil entero donde se almacena el numero de filas de pixeles de la imagen
      col entero donde se almacena el numero de columnas de pixeles de la imagen
*/

imagen* crearImg(int fil,int col){
     imagen *I;
     int i;
     if ((I=(imagen*)malloc(sizeof(imagen)))==NULL){
        printf ("No se reservo memoria para la imagen");
        exit(1);
     }                                                   
     
        if (((I->pix)=(pixel**)malloc(sizeof(pixel*)*fil+1))==NULL){
             printf("No se reservo memoria para la imagen");
             exit(1);                                                         
        }
        for(i=0;i<fil;i++){
            if (((I->pix[i])=(pixel*)malloc(sizeof(pixel)*col+1))==NULL){
             printf("No se reservo memoria para la imagen");
             exit(1);                                                         
             }
          }
        I->fil=fil;
        I->col=col;
                     
         return I; 
}

/* Esta función guarda una imagen en gdImagePtr en imagen*. 
   Recibe:
      im gdImagePtr donde se encuentra almacenada la imagen con la que se trabaja
*/

imagen* deImagePtr(gdImagePtr im){
        imagen *img;
        int p,f,c,i,j;
        f=gdImageSY(im);
        c=gdImageSX(im);
        img=crearImg(f,c);
        for (i=0;i<f;i++){
            for (j=0;j<c;j++){       
                p=gdImageGetPixel(im,j,i);
                img->pix[i][j].r=gdImageRed(im,p);
                img->pix[i][j].g=gdImageGreen(im,p);
                img->pix[i][j].b=gdImageBlue(im,p);
            }
        }
        return img;
}

/* Esta función abre una imagen de fichero y la guarda en gdImagePtr.
   Recibe:
      nfich puntero a char donde se almacena el nombre de la imagen que se desea usar
*/
   
gdImagePtr abrirImagen(char *nfich){
           FILE *fix;
           gdImagePtr im;
           if ((fix=fopen(nfich,"rb"))==NULL){
              perror("No se pudo abrir la imagen1");
              exit(1);                                     
           }
           im=gdImageCreateFromJpeg(fix);
           fclose(fix); 
           return im;
           }

/* Esta función guarda una imagen en imagen* en gdImagePtr. 
   Recibe:
      img imagen* donde se encuentra almacenada la imagen que se obtiene como resultado
*/
           
gdImagePtr deImagen(imagen *img){
     int i,j,p;
     gdImagePtr im;
     im = gdImageCreateTrueColor((img->col),(img->fil));
     for (i=0;i<(img->fil);i++){
         for(j=0;j<(img->col);j++){         
            p = gdImageColorAllocate(im, img->pix[i][j].r, img->pix[i][j].g, img->pix[i][j].b);
            gdImageSetPixel(im, j, i,p);
         }
     }
     return im;
}

/* Esta función pasa a escala de grises una imagen gdImagePtr y 
   la guarda en imagen*.
   Recibe:
      im gdImagePtr donde se encuentra almacenada la imagen con la que se trabaja
      img imagen* donde se encuentra almacenada la imagen que se obtiene como resultado de la funcion
*/

void conv_gris (gdImagePtr im,imagen *img){
     int i,j,p;
     for (i=0;i<(img->fil);i++){
         for(j=0;j<(img->col);j++){
            p=gdImageGetPixel(im,j,i);
            img->pix[i][j].r=gdImageRed(im,p)*0.3+gdImageGreen(im,p)*0.59+gdImageBlue(im,p)*0.11;
            img->pix[i][j].g=gdImageRed(im,p)*0.3+gdImageGreen(im,p)*0.59+gdImageBlue(im,p)*0.11;
            img->pix[i][j].b=gdImageRed(im,p)*0.3+gdImageGreen(im,p)*0.59+gdImageBlue(im,p)*0.11;                       
            }
         }
     }

/* Esta función guarda en un archivo .jpg, que recibe el nombre que 
   se le pasa a la función, la imagen que se le pasa a la función. 
   Recibe:
      nfich puntero a char donde se almacena el nombre de la imagen en la que se desea guardar
      im gdImagePtr donde se encuentra almacenada la imagen que se obtiene como resultado
*/

void escribirImagen(char*nfich,gdImagePtr im){
     FILE *fich;
     if ((fich=fopen(nfich,"wb"))==NULL){
       perror("No se pudo abrir la imagen2");
       exit(1);
       }
    gdImageJpeg(im, fich, -1);
    fclose(fich);
     }
     
/* Esta función libera el contenido de las imagenes* definidas. 
   Recibe:
      img imagen* donde se encuentra almacenada la imagen que se obtiene como resultado de la funcion
*/ 
     
void liberarImagen(imagen *img){
     int i;
     for(i=0;i<(img->fil);i++){
        free((img->pix[i]));
     }
     free(img->pix);
     free(img);
     }
     
/* Esta función cambia el orden de las columnas colocando la 
   primera la ultima y la ultima la primera, asi sucesivamente.
   Recibe:
      img imagen* donde se encuentra almacenada la imagen que se obtiene como resultado de la funcion
      tamf entero donde se almacena el tamaño en filas de la imagen
      tamc entero donde se almacena el tamaño en columnas de la imagen
*/  
     
void espjhorizont (imagen *img,int tamf,int tamc){
     int i,j;
     pixel aux;
     for (i=0;i<tamf;i++){
         for (j=0;j<tamc/2;j++){
             aux=img->pix[i][j];
             img->pix[i][j]=img->pix[i][tamc-1-j];
             img->pix[i][tamc-1-j]=aux;
         }
    } 
        
}       

/* Esta función cambia el orden de las filas colocando la 
   primera la ultima y la ultima la primera, asi sucesivamente.
   Recibe:
      img imagen* donde se encuentra almacenada la imagen que se obtiene como resultado de la funcion
      tamf entero donde se almacena el tamaño en filas de la imagen
      tamc entero donde se almacena el tamaño en columnas de la imagen
*/  

void espjvertic (imagen *img,int tamf,int tamc){
     int i,j;
     pixel aux;
     for (i=0;i<tamf/2;i++){
         for (j=0;j<tamc;j++){
             aux=img->pix[i][j];
             img->pix[i][j]=img->pix[tamf-1-i][j];
             img->pix[tamf-1-i][j]=aux;
         }
    }
    
}

/* Esta función realiza un zoom de una zoona designada.
   Recibe:
      img imagen* donde se encuentra almacenada la imagen a la que se le realiza el zoom
      I   imagen* donde se encuentra almacenada la imagen que se obtiene como resultado de la funcion
      e   entero donde se almacena la coordenada x de la esquina superior de la zona para el zoom 
      f   entero donde se almacena la coordenada y de la esquina superior de la zona para el zoom
      g   entero donde se almacena la coordenada x de la esquina inferior de la zona para el zoom
      h   entero donde se almacena la coordenada y de la esquina inferior de la zona para el zoom
*/  

imagen* zumy(int e,int f,int g,int h,int factor,imagen *I,imagen *img){
        int i,j,zoom_f,factor_f,factor_c,zoom_c;
        for (i = e, zoom_f = 0; i <= g; i++)
            for (factor_f = 0; factor_f < factor; factor_f++, zoom_f++)
                for (j = f, zoom_c = 0; j <= h; j++)
                    for (factor_c = 0; factor_c < factor; factor_c++, zoom_c++)        
                    {
                        I->pix[zoom_f][zoom_c]=img->pix[i][j];
                    }
        return I;
}

/* Esta función crea una imagen* para guardar la imagen resultante del zoom y realiza un zoom de una zoona designada.
   Recibe:
      img imagen* donde se encuentra almacenada la imagen a la que se le realiza el zoom
      x1   entero donde se almacena la coordenada x de la esquina superior de la zona para el zoom 
      y1   entero donde se almacena la coordenada y de la esquina superior de la zona para el zoom
      x2  entero donde se almacena la coordenada x de la esquina inferior de la zona para el zoom
      y2  entero donde se almacena la coordenada y de la esquina inferior de la zona para el zoom
*/  

imagen* zoom (imagen *img,int tamf,int tamc,int factor,int y1,int x1,int y2,int x2){
     imagen *I;
     I = crearImg((x2-x1+1)*factor,(y2-y1+1)*factor);
     I=zumy(x1,y1,x2,y2,factor,I,img);
     return I;
}

/* Esta función rellena una imagen pasandole los valores de
   los colores de los pixeles por teclado.
   Recibe:
      img imagen* donde se encuentra almacenada la imagen a la que se le realiza el zoom
      fil entero donde se almacena el numero de filas de pixeles de la imagen
      col entero donde se almacena el numero de columnas de pixeles de la imagen
      */  

void rellenar_imagen (int fil,int col,imagen *img){
     int i,j;
        for (i=0;i<fil;i++){
            for (j=0;j<col;j++){
                printf ("Intro color pixel[%d/%d]\n",i,j);
                printf ("rojo: ");
                do{
                   scanf ("%d",&img->pix[i][j].r);
                }while((img->pix[i][j].r<0)||(img->pix[i][j].r>255));
                printf ("verde: ");
                do{
                   scanf ("%d",&img->pix[i][j].g);
                }while((img->pix[i][j].g<0)||(img->pix[i][j].g>255));
                printf ("azul: ");
                do{
                   scanf ("%d",&img->pix[i][j].b);
                }while((img->pix[i][j].b<0)||(img->pix[i][j].b>255));
                }
            }
        }
        
/*Esta función rellena una imagen entera del color que s e le a pasando.
   Recibe:
      img imagen* donde se encuentra almacenada la imagen a la que se le realiza el zoom
      fil entero donde se almacena el numero de filas de pixeles de la imagen
      col entero donde se almacena el numero de columnas de pixeles de la imagen
      r   entero en el que se guarda el grado de rojo
      v   entero en el que se guarda el grado de verde
      a   entero en el que se guarda el grado de azul
      */     

void dar_color (int fil,int col,imagen *img,int r,int v,int a){
     int i,j;
     for (i=0;i<fil;i++){
            for (j=0;j<col;j++){
                img->pix[i][j].r=r;
                img->pix[i][j].g=v;
                img->pix[i][j].b=a;
            }
     }
}

/* Esta función abre un archivo que recibe la funcion y crea una
   imagen del tamaño definido dentro del archivo que se rellena 
    con la matriz que contiene el archivo. 
   Recibe:
      nfich puntero a char donde se almacena el nombre del fichero que se desea abrir
*/

imagen* leerFichero(char *nfich){
     FILE *fich;
     int i,j,fil,col;
     imagen* I;
     if((fich=fopen(nfich,"r"))==NULL){
        perror("No se puede abrir el fichero");
        exit(1);                              
     }
     else{
          fscanf(fich,"%d",&fil);
          fscanf(fich,"%d",&col);
          I=crearImg (fil,col);
          for (i=0;i<(I->fil);i++){
            for (j=0;j<(I->col);j++){
                fscanf(fich,"%d",&I->pix[i][j].r);          
              }
             }    
          }
     fclose(fich);
     return I;
}

/* Esta función convierte una matriz en una imagen. 
   Recibe:
      m matriz de enteros en la que se almacena una matriz para convertir en una imagen
*/

imagen* conv_img(int m[3][3]){
        int i,j,fil,col;
        imagen* I;
        fil=3;
        col=3;
        I=crearImg(fil,col);
        for (i=0;i<3;i++){
            for (j=0;j<3;j++){
                I->pix[i][j].r=m[i][j];
            }
        }
        return I;
}

/* Esta función convoluciona dos imagenes, es decir, aplica
   a una imagen otra imagen que la modifica pero manteniendo
   parte de su forma original.
   Recibe:
      img imagen* donde se encuentra almacenada la imagen sobre la que se va a trabajar
      img2 imagen* donde se encuentra almacenada la imagen que usaremos para modificar la otra imagen
*/

imagen* convolucion (imagen *img,imagen *img2){
        imagen *I;
        int i,j,v,u,tamY,tamX,suma;
        tamY=img->col+img2->col-1;
        tamX=img->fil+img2->fil-1;
        I=crearImg(tamX,tamY);
        for (i=0;i<tamX;i++){
            for (j=0;j<tamY;j++){
                suma=0;
                for (u=0;u<(img2->fil);u++){
                    for (v=0;v<(img2->col);v++){
                        if ((i-u>=0)&&(j-v>=0)&&(i-u<(img->fil))&&(j-v<(img->col))){
                           suma=suma+((img->pix[i-u][j-v].r)*(img2->pix[u][v].r));
                        }
                        }
                    }
                I->pix[i][j].r=I->pix[i][j].g=I->pix[i][j].b=suma;
                }
           }
        return I;
}

/* Esta función realiza el gradiente a una imagen a partir de las 
   convoluciones obtenidas como resultado de aplicarles dos imagenes
   distintas, es decir, las convoluciones Iv y Ih a las que aplica
   una pequeña formula para obtener una imagen gradiente de la original, 
   que es guardada en una imagen* y devuelta por la función.
   Recibe:
      Iv imagen* donde se encuentra almacenada la convolucion vertical
      Ih imagen* donde se encuentra almacenada la convolucion horizontal
*/

imagen* gradiente(imagen* Iv,imagen* Ih){
        int i,j,vertical,horizontal;
        imagen* I;
        I=crearImg(Iv->fil,Iv->col);
        for (i=0;i<(Iv->fil);i++){
            for (j=0;j<(Iv->col);j++){
                vertical=(Iv->pix[i][j].r)*(Iv->pix[i][j].r);
                horizontal=(Ih->pix[i][j].r)*(Ih->pix[i][j].r);
                I->pix[i][j].r=sqrt((float)(vertical+horizontal));
                I->pix[i][j].b=I->pix[i][j].g=I->pix[i][j].r;
                }
        }
        return I;
}

/* Esta función realiza una umbralizacion de una imagen al recibir
   la imagen original y un umbral entre 0 y 1 para obtener un resutado
   esperado. Ademas, crea una imagen* donde se guarda  el resultado de 
   la umbralizacion que es devuelta por la funcion.  
   Recibe:
      I   imagen* donde se encuentra almacenada la imagen a la que se le realiza la umbralizacion
      IMG imagen* donde se encuentra almacenada la imagen que se obtiene como resultado de la funcion
*/
      
imagen* umbral(imagen* I,double umb){
        int  i,j,max=0,num_umbral;
        imagen* IMG;
        IMG=crearImg(I->fil,I->col);
        for (i=0;i<(I->fil);i++){
            for (j=0;j<(I->col);j++){
                if (I->pix[i][j].r>max)
                   max=I->pix[i][j].r;
                }
            }
        num_umbral=umb*max;
        for (i=0;i<(I->fil);i++){
            for (j=0;j<(I->col);j++){
                if (I->pix[i][j].r>=num_umbral)
                   IMG->pix[i][j].r=1;
                else
                    IMG->pix[i][j].r=0;
                }
            }
        return IMG;
}

/* Esta función realiza un recorte a una imagen.  
   Recibe:
      I   imagen* donde se encuentra almacenada la imagen que se quiere recortar
      col2 entero donde se almacena el tamaño  en columnas de una imagen anteriormente usada (usada como kernel para la convolucion)
      fil2 entero donde se almacena el tamaño en filas de una imagen anteriormente usada (usada como kernel para la convolucion)
      col entero donde se almacena el tamño en columnas de una imagen anteriormente usada (la imagen original)
      fil entero donde se almacena el tamño en filas de una imagen anteriormente usada (la imagen original)
*/

imagen* recorte (imagen* I,int col2,int fil2,int col,int fil){
        int i,j,tamX,tamY;
        imagen* IMG;
        tamX=col2/2;
        tamY=fil2/2;
        IMG=crearImg(I->fil,I->col);
        for (i=0;i<fil;i++){
            for (j=0;j<col;j++){
                IMG->pix[i][j].r=I->pix[i+tamY][j+tamX].r;                
            }
        }
        return IMG;
}

/* Esta funcion realiza una normalizacion de una imagen.
   Recibe:
      col entero donde se almacena el tamño en columnas de la imagen
      fil entero donde se almacena el tamño en filas de la imagen        
*/
        
void normaliza(imagen* IMG){
     int i,j,max=0,min=255;
     for (i=0;i<(IMG->fil);i++){
            for (j=0;j<(IMG->col);j++){
                if ((IMG->pix[i][j].r)>max){
                   max=IMG->pix[i][j].r;
                   }
                if ((IMG->pix[i][j].r)<min){
                   min=IMG->pix[i][j].r;
                   }
            }
     }
     for (i=0;i<(IMG->fil);i++){
            for (j=0;j<(IMG->col);j++){
                IMG->pix[i][j].r=(IMG->pix[i][j].r-min)*255/(max-min);
                IMG->pix[i][j].b=IMG->pix[i][j].g=IMG->pix[i][j].r;
                }
            }
}

/* Esta funcion pide el tamaño de una imagen.
   Recibe:
      IMG imagen* donde se encunetra almacenada la imagen que queremos almacenar         
*/

void tam (int *fil,int *col){
     int y1,x1,dev;
     do{
       dev=0;
       printf ("Intro. num de filas \n");
       scanf("%d\n",&*fil);
       printf ("Intro. num de columnas\n ");
       scanf("%d\n",&*col);
       x1=*fil;
       y1=*col;
       x1=*fil%2;
       y1=*col%2;
       if ((x1==0)||(y1==0)){
          perror ("el tamaño de las filas y las columnas ha de ser impar");           
          dev=1;
          }
       if ((*fil<3)||(*fil>11)||(*col<3)||(*col>11)){
          perror ("el tamaño a de ser impar entre 3 y 11");
          dev=1;
          }
    }while(dev==1);
}

void borrador(imagen* img){
     int i,j,umbral;
     do{
        printf ("Intro. umbral 0-255\n");
        scanf ("%d",&umbral);
     }while((umbral<0)||(umbral>255));
     for (i=0;i<img->fil;i++){
         for (j=0;j<img->col;j++){
             if ((img->pix[i][j].r)>umbral){
                img->pix[i][j].r=255;
                img->pix[i][j].g=255;
                img->pix[i][j].b=255;
                }
             }
         }
     
     }
imagen* adaptador(imagen* I,imagen* img){
     int filas,columnas,i,j,e,d;
     imagen* IMG;
     filas=(I->fil)-(img->fil);
     columnas=(I->col)-(img->col);
     filas=filas/2;
     columnas=columnas/2;
     IMG=crearImg(img->fil,img->col);
     for (e=0,i=filas;i<I->fil-filas;i++,e++){
         for (d=0,j=columnas;j<I->col-columnas;j++,d++){
             IMG->pix[e][d].r=I->pix[i][j].r;
             IMG->pix[e][d].g=I->pix[i][j].g;
             IMG->pix[e][d].b=I->pix[i][j].b;
             }
         }
     return IMG;
     }
     
void fusion (imagen*IMG,imagen*img){
     int i,j;
     for (i=0;i<img->fil;i++){
         for (j=0;j<img->col;j++){
             if (img->pix[i][j].r==255){
                img->pix[i][j].r=IMG->pix[i][j].r;
                img->pix[i][j].g=IMG->pix[i][j].g;
                img->pix[i][j].b=IMG->pix[i][j].b;
                }
             }
         }
     }
